package com.eksframework.commons.core.base;

import com.google.common.collect.Lists;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 列表类
 *
 * @author chenfenghai
 * @date 2019-12-11 15:46
 */
public class EksList {

    /**
     * Constructor.
     */
    EksList() {
    }

    /**
     * 分组，即把列表分成count组
     *
     * <pre>
     * List<T> list = Lists.newArrayList(1,2,3,4,5);
     * EksList.group(null, 1)                   = []
     * EksList.group(Lists.newArrayList(), 1)   = []
     * EksList.group(list, 0)                   = IllegalArgumentException
     * EksList.group(list, 1)                   = [[1,2,3,4,5]]
     * EksList.group(list, 2)                   = [[1,2,3],[4,5]]
     * EksList.group(list, 3)                   = [[1,2],[3,4],[5]]
     * EksList.group(list, 4)                   = [[1,2],[3],[4],[5]]
     * EksList.group(list, 5)                   = [[1],[2],[3],[4],[5]]
     * EksList.group(list, 6)                   = [[1],[2],[3],[4],[5]]
     * </pre>
     *
     * @param list  列表
     * @param count 组数量
     * @param <T>   对象
     * @return List-List-T
     * @throws IllegalArgumentException – if count is nonpositive
     */
    public static <T> List<List<T>> group(List<T> list, int count) {
        if (CollectionUtils.isEmpty(list)) {
            return Lists.newArrayList();
        }
        if (count <= 0) {
            throw new IllegalArgumentException("count: " + count);
        }
        if (count >= list.size()) {
            return Lists.partition(list, 1);
        }
        List<List<T>> groupList = Lists.newArrayList();
        int totalSize = list.size();
        int groupSize = totalSize / count;
        int remainCount = totalSize % count;
        int startIndex = 0;
        for (int i = 0; i < count; i++) {
            int endIndex = startIndex + groupSize;
            if (i < remainCount) {
                endIndex++;
            }
            if (endIndex > totalSize) {
                endIndex = totalSize;
            }
            groupList.add(list.subList(startIndex, endIndex));
            startIndex = endIndex;
        }
        return groupList;
    }

    /**
     * 交集，即在A集合中存在并且在B集合中也存在的元素，根据List#contains判断是否相同元素
     * A: [1,2,3,4]
     * B: [3,4,5,6]
     * intersection(A, B): [3,4]
     *
     * @param first  列表1
     * @param second 列表2
     * @param <T>    对象
     * @return List-T
     */
    public static <T> List<T> intersection(List<T> first, List<T> second) {
        if (CollectionUtils.isEmpty(first) || CollectionUtils.isEmpty(second)) {
            return Lists.newArrayList();
        }
        return first.stream().filter(second::contains).collect(Collectors.toList());
    }

    /**
     * 交集，即在A集合中存在并且在B集合中也存在的元素，根据keyExtractor判断是否相同元素
     * A: [1,2,3,4]
     * B: [3,4,5,6]
     * intersection(A, B, Function): [3,4]
     *
     * @param first        列表1
     * @param second       列表2
     * @param keyExtractor 主键属性，参考Set#contains
     * @param <T>          对象
     * @return List-T
     */
    public static <T, R> List<T> intersection(List<T> first, List<T> second, Function<T, R> keyExtractor) {
        if (CollectionUtils.isEmpty(first) || CollectionUtils.isEmpty(second)) {
            return Lists.newArrayList();
        }
        Set<R> keyExtractorSet = second.stream().map(keyExtractor)
                .collect(Collectors.toCollection(HashSet::new));
        return first.stream().filter(t -> keyExtractorSet.contains(keyExtractor.apply(t)))
                .collect(Collectors.toList());
    }

    /**
     * 并集，即包含A集合和B集合的所有元素，不去重复，可能存在重复元素
     * A: [1,2,3,4]
     * B: [3,4,5,6]
     * union(A, B): [1,2,3,4,3,4,5,6]
     *
     * @param first  列表1
     * @param second 列表2
     * @param <T>    对象
     * @return List-T
     */
    public static <T> List<T> union(List<T> first, List<T> second) {
        if (CollectionUtils.isEmpty(first) && CollectionUtils.isEmpty(second)) {
            return Lists.newArrayList();
        }
        List<T> result = Lists.newArrayList();
        if (!CollectionUtils.isEmpty(first)) {
            result.addAll(first);
        }
        if (!CollectionUtils.isEmpty(second)) {
            result.addAll(second);
        }
        return result;
    }

    /**
     * 并集，即包含A集合和B集合的所有元素，并根据deduplicate条件是否去掉重复元素
     * A: [1,2,3,4]
     * B: [3,4,5,6]
     * union(A, B, true): [1,2,3,4,5,6]
     * union(A, B, false): [1,2,3,4,3,4,5,6]
     *
     * @param first       列表1
     * @param second      列表2
     * @param deduplicate 是否去掉重复
     * @param <T>         对象
     * @return List-T
     */
    public static <T> List<T> union(List<T> first, List<T> second, boolean deduplicate) {
        if (deduplicate) {
            return union(first, second).stream().distinct().collect(Collectors.toList());
        }
        return union(first, second);
    }

    /**
     * 并集，即包含A集合和B集合的所有元素，并根据keyExtractor进行去掉重复元素
     * A: [1,2,3,4]
     * B: [3,4,5,6]
     * union(A, B, Function): [1,2,3,4,5,6]
     *
     * @param first        列表1
     * @param second       列表2
     * @param keyExtractor 主键属性，参考Objects.equals
     * @param <T>          对象
     * @return List-T
     */
    public static <T, U extends Comparable<? super U>> List<T> union(
            List<T> first, List<T> second, Function<? super T, ? extends U> keyExtractor) {
        return union(first, second).stream().collect(
                Collectors.collectingAndThen(
                        Collectors.toCollection(
                                () -> new TreeSet<>(Comparator.comparing(keyExtractor))), ArrayList<T>::new));
    }

    /**
     * 差集，即在A集合存在而在B集合不存在的元素
     * A: [1,2,3,4]
     * B: [3,4,5,6]
     * subtract(A, B): [1,2]
     * subtract(B, A): [5,6]
     *
     * @param first  列表1
     * @param second 列表2
     * @param <T>    对象
     * @return List-T
     */
    public static <T> List<T> subtract(List<T> first, List<T> second) {
        if (CollectionUtils.isEmpty(first)) {
            return first == null ? Lists.newArrayList() : first;
        }
        if (CollectionUtils.isEmpty(second)) {
            return first;
        }
        return first.stream().filter(t -> !second.contains(t)).collect(Collectors.toList());
    }

    /**
     * 差集，即在A集合存在而在B集合不存在的元素，根据keyExtractor判断是否相同元素
     * A: [1,2,3,4]
     * B: [3,4,5,6]
     * subtract(A, B): [1,2]
     * subtract(B, A): [5,6]
     *
     * @param first        列表1
     * @param second       列表2
     * @param keyExtractor 主键属性，参考Set#contains
     * @param <T>          对象
     * @return List-T
     */
    public static <T, R> List<T> subtract(List<T> first, List<T> second, Function<T, R> keyExtractor) {
        if (CollectionUtils.isEmpty(first)) {
            return first == null ? Lists.newArrayList() : first;
        }
        if (CollectionUtils.isEmpty(second)) {
            return first;
        }
        Set<R> keyExtractorSet = second.stream().map(keyExtractor)
                .collect(Collectors.toCollection(HashSet::new));
        return first.stream().filter(t -> !keyExtractorSet.contains(keyExtractor.apply(t)))
                .collect(Collectors.toList());
    }

    /**
     * 交集的补集（析取），即集合A与集合B中所有不属于A∩B（交集）的元素的集合
     * A: [1,2,3,4]
     * B: [3,4,5,6]
     * disjunction(A, B): [1,2,5,6]
     *
     * @param first  列表1
     * @param second 列表2
     * @param <T>    对象
     * @return List-T
     */
    public static <T> List<T> disjunction(List<T> first, List<T> second) {
        if (CollectionUtils.isEmpty(first) && CollectionUtils.isEmpty(second)) {
            return Lists.newArrayList();
        }
        if (CollectionUtils.isEmpty(first)) {
            return second;
        }
        if (CollectionUtils.isEmpty(second)) {
            return first;
        }
        List<T> result = Lists.newArrayList();
        result.addAll(first.stream().filter(t -> !second.contains(t)).collect(Collectors.toList()));
        result.addAll(second.stream().filter(t -> !first.contains(t)).collect(Collectors.toList()));
        return result;
    }

    /**
     * 交集的补集（析取），即集合A与集合B中所有不属于A∩B（交集）的元素的集合
     * A: [1,2,3,4]
     * B: [3,4,5,6]
     * disjunction(A, B, Function): [1,2,5,6]
     *
     * @param first        列表1
     * @param second       列表2
     * @param keyExtractor 主键属性，参考Set#contains
     * @param <T>          对象
     * @return List-T
     */
    public static <T, R> List<T> disjunction(List<T> first, List<T> second, Function<T, R> keyExtractor) {
        if (CollectionUtils.isEmpty(first) && CollectionUtils.isEmpty(second)) {
            return Lists.newArrayList();
        }
        if (CollectionUtils.isEmpty(first)) {
            return second;
        }
        if (CollectionUtils.isEmpty(second)) {
            return first;
        }
        List<T> result = Lists.newArrayList();
        result.addAll(intersection(first, second, keyExtractor));
        result.addAll(intersection(second, first, keyExtractor));
        return result;
    }

}
